package org.apache.ibatis.common.logging.jdbc;

import org.apache.ibatis.common.logging.Log;
import org.apache.ibatis.common.reflection.ExceptionUtil;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.ResultSet;
import java.sql.Statement;

/**
 * @Name StatementLogger
 * @Description: [JDBC Statement 对象代理（日志增强）] 实现类
 * @Version v1.0.0
 * @Author: lichao
 * @Date: 2024-05-15 17:56:14
 **/
public class StatementLogger extends BaseJdbcLogger implements InvocationHandler {
    private final Statement statement;

    private StatementLogger(Statement statement, Log statementLog, int queryStack) {
        super(statementLog, queryStack);
        this.statement = statement;
    }

    public static Statement newInstance(Statement statement, Log statementLog, int queryStack) {
        InvocationHandler handler = new StatementLogger(statement, statementLog, queryStack);
        ClassLoader loader = StatementLogger.class.getClassLoader();
        return (Statement) Proxy.newProxyInstance(loader, new Class[]{StatementLogger.class}, handler);
    }

    public Statement getStatement() {
        return statement;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 如果方法继承自 Object 类，交由当前对象执行
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
            // 拦截 Statement 中的 execute、executeUpdate、executeQuery、executeQuery 方法
            if (EXECUTE_METHODS.contains(method.getName())) {
                // 打印日志
                if (isDebugEnabled()) debug(" Executing: " + removeBreakingWhitespace((String) args[0]), true);
                // 如果目标方法为 executeQuery 方法，返回 ResultSet 代理对象，该代理中加入了对 ResultSet 的日志打印操作
                if ("executeQuery".equals(method.getName())) {
                    ResultSet rs = (ResultSet) method.invoke(statement, args);
                    return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
                } else return method.invoke(statement, args);
            }
            // 拦截 Statement 中的 getResultSet 方法
            else if ("getResultSet".equals(method.getName())) {
                // 返回 ResultSet 代理对象，该代理中加入了对 ResultSet 的日志打印操作
                ResultSet rs = (ResultSet) method.invoke(statement, args);
                return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
            }
            // 其他方法，直接放行
            else {
                return method.invoke(statement, args);
            }
        } catch (Throwable e) {
            throw ExceptionUtil.unwrapThrowable(e);
        }
    }
}

